package com.alibaba.dubbo.remoting.http.servlet;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.dubbo.common.utils.MimeTypeUtils;

public class StaticResourceServlet extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	static StaticResourceServlet INSTANCE;
	
	public static StaticResourceServlet getInstance() {
    	return INSTANCE;
    }
    
    public StaticResourceServlet() {
    	StaticResourceServlet.INSTANCE = this;
    }

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String path = req.getRequestURI();
		if(cache.containsKey(path)){
			WeakReference<ResourceCache> ref = cache.get(path);
			if(ref != null && ref.get() != null){
				ResourceCache resource  = ref.get();
				File file = new File(resource.file);
				//文件被删除，长度不匹配，修改日期不匹配，则认为过期
				if(!file.exists() || !file.isFile() || file.length() != resource.length
						|| file.lastModified() != resource.time){
					resource = null;
				}
				
				if(resource != null){
					OutputStream toClient = new BufferedOutputStream(resp.getOutputStream());
					toClient.write(resource.data);
					toClient.flush();
					toClient.close();
					return;
				}
			}
			cache.remove(path);
		}
		
		for(Map.Entry<String, String> entry : mapping.entrySet()){
			String vpath = entry.getKey();
			String rpath = entry.getValue();
			if(path.startsWith(vpath)){
				//get the real path
				String x = path.substring(vpath.length());
				if(!File.separator.equals("/")){
					x = x.replace("/", File.separator);
				}
				rpath = rpath + x;
				File file = new File(rpath);
				if(file.exists() == false || file.isFile() == false) break;
				FileInputStream is = new FileInputStream(file);
				resp.reset();
				resp.setContentType(MimeTypeUtils.getMimeType(file));
				resp.addHeader("Content-Disposition", "attachment; filename=" + file.getName());  
				resp.setContentLength((int) file.length());
				byte []data = new byte[(int) file.length()];
				int len = 0;
				OutputStream toClient = new BufferedOutputStream(resp.getOutputStream());
				if((len = is.read(data)) > 0){
					toClient.write(data, 0, len);
				}
				is.close();
				is = null;
				toClient.flush();
				toClient.close();
				ResourceCache resource = new ResourceCache();
				resource.time = file.lastModified();
				resource.length = file.length();
				resource.file = rpath;
				resource.data = data;
				cache.put(path, new WeakReference<ResourceCache>(resource));
				return;
			}
		}
		
		resp.sendError(HttpServletResponse.SC_NOT_FOUND, null);
	}
	
	static class ResourceCache{
		String file;
		long time;
		long length;
		byte [] data;
	}
	
	private static final Map<String, String> mapping = new HashMap<String, String>();
	private static final Map<String, WeakReference<ResourceCache>> cache = new ConcurrentHashMap<String, WeakReference<ResourceCache>>();

	/**
	 *  add mapping for virtual path to real path
	 * @param vpath virtual path
	 * @param rpath real path
	 */
	static public void addServletMapping(String vpath, String rpath){
		mapping.put(vpath, rpath);
	}
}
